/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.volatileengine.renderer.lwjgl;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;

import org.lwjgl.opengl.EXTFramebufferObject;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GLContext;

import com.volatileengine.image.Mipmap;
import com.volatileengine.resources.references.Handle;
import com.volatileengine.scene.Surface;

/**
 * 
 * @author Administrator
 */
class LWJGLFramebufferObjectTextureRenderer {

	private int id;
	private int depthRenderBufferId;
	private IntBuffer buffer = ByteBuffer.allocateDirect(16 * 4).order(ByteOrder.nativeOrder()).asIntBuffer();

	public void initialise() throws UnsupportedOperationException {
		if (!isSupported()) {
			throw new UnsupportedOperationException("FramebufferObjects not supported by this platform");
		}
		buffer.clear();
		buffer.limit(1);
		EXTFramebufferObject.glGenFramebuffersEXT(buffer);
		id = buffer.get();

		buffer.clear();
		buffer.limit(1);
		EXTFramebufferObject.glGenRenderbuffersEXT(buffer);
		depthRenderBufferId = buffer.get();
	}

	public void destroy() {
		if (!isSupported()) {
			return;
		}
		buffer.clear();
		buffer.put(depthRenderBufferId).flip();
		EXTFramebufferObject.glDeleteRenderbuffersEXT(buffer);

		buffer.clear();
		buffer.put(id).flip();
		EXTFramebufferObject.glDeleteFramebuffersEXT(buffer);
	}

	public void activate() {
		if (isSupported()) {
			EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, id);
		}
	}

	public void deactivate() {
		if (isSupported()) {
			EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, 0);
		}
	}

	public void setSurfaces(Surface[] surfaces) throws UnsupportedOperationException {
		if (!isSupported()) {
			throw new UnsupportedOperationException();
		}
		activate();
		boolean foundDepth = false;
		boolean foundColor = false;
		for (int i = 0; i < surfaces.length; i++) {
			switch (surfaces[i].getSurfaceCapability()) {
			case DEPTH:
				foundDepth = true;
				break;
			case COLOUR_0:
			case COLOUR_1:
			case COLOUR_2:
			case COLOUR_3:
			case COLOUR_4:
				foundColor = true;
				break;
			case NONE:
				throw new IllegalArgumentException(
						"Cannot render to a Surface without a Capability (e.g. WindowSurface) through a TextureRenderer");
			case MULTIPLE:
				throw new IllegalArgumentException(
						"Cannot render to a surface with multiple Capababilities (e.g. CompositeSurface) through a TextureRenderer");
			}

			switch (surfaces[i].getHandle().getHandleType()) {
			case MIPMAP:
				Handle<?> handle = surfaces[i].getHandle();
				EXTFramebufferObject.glFramebufferTexture2DEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT,
						convertAttachmentPoint(surfaces[i].getSurfaceCapability()), GL11.GL_TEXTURE_2D, handle
								.getIdentity(), ((Mipmap) handle.getResource()).getMipmapLevel());
				break;
			}
		}

		if (!foundDepth) {
			EXTFramebufferObject.glFramebufferRenderbufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT,
					EXTFramebufferObject.GL_DEPTH_ATTACHMENT_EXT, EXTFramebufferObject.GL_RENDERBUFFER_EXT,
					depthRenderBufferId);
		}

		if (!foundColor) {
			GL11.glDrawBuffer(GL11.GL_NONE);
			GL11.glReadBuffer(GL11.GL_NONE);
		}

		if (!foundColor && foundDepth) {
			GL11.glDrawBuffer(GL11.GL_NONE);
		}
		deactivate();
	}

	private int convertAttachmentPoint(Surface.SurfaceCapability point) {
		switch (point) {
		case DEPTH:
			return EXTFramebufferObject.GL_DEPTH_ATTACHMENT_EXT;
		case STENCIL:
			return EXTFramebufferObject.GL_STENCIL_ATTACHMENT_EXT;
		default:
			return EXTFramebufferObject.GL_COLOR_ATTACHMENT0_EXT + point.ordinal();
		}
	}

	public boolean isSupported() {
		return GLContext.getCapabilities().GL_EXT_framebuffer_object;
	}
}
